package com.fw.hander;


import com.fw.core.domain.AjaxResult;
import com.fw.exception.BaseException;
import com.fw.exception.CustomException;
import com.fw.exception.DemoModeException;
import com.fw.mes.Result;
import com.fw.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.fw.enums.CodeStatus.REALNAME_INCORRECT;


@RestControllerAdvice
@Slf4j(topic = "exception")
public class GlobalExceptionHandler {

/*    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(UserTokenException.class)
    public Object userTokenExceptionHandler(UserTokenException ex) {
        StackTraceElement[] els = ex.getStackTrace();
        String msg = exToMsg(els);
        log.error("{}:{}", ex.getMessage(), msg);
        return new Result<>().fail(CommonResultConstant.AUTH_ERROR.getErrorCode(), ex.getMessage());
    }*/

    private String exToMsg(StackTraceElement[] els) {
        String msg;
        StringBuffer msgBuffer = new StringBuffer();
        if (els != null && els.length > 0) {
            StackTraceElement row1 = els[0];
            msgBuffer.append(row1.getClassName());
            msgBuffer.append(" : ");
            msgBuffer.append(row1.getMethodName());
            msgBuffer.append(" -> ");
            msgBuffer.append(row1.getLineNumber());
            msgBuffer.append(" :: ");
        }
        msg = msgBuffer.toString();
        return msg;
    }

    /**
     * 基础异常
     */
    @ExceptionHandler(BaseException.class)
    public AjaxResult baseException(BaseException e)
    {
        return AjaxResult.error(e.getMessage());
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(CustomException.class)
    public AjaxResult businessException(CustomException e)
    {
        if (StringUtils.isNull(e.getCode()))
        {
            return AjaxResult.error(e.getMessage());
        }
        return AjaxResult.error(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public AjaxResult handlerNoFoundException(Exception e)
    {
        log.error(e.getMessage(), e);
        return AjaxResult.error(com.fw.constant.HttpStatus.NOT_FOUND, "路径不存在，请检查路径是否正确");
    }

/*    @ExceptionHandler(AccessDeniedException.class)
    public AjaxResult handleAuthorizationException(AccessDeniedException e)
    {
        log.error(e.getMessage());
        return AjaxResult.error(com.fw.constant.HttpStatus.FORBIDDEN, "没有权限，请联系管理员授权");
    }*/

    @ExceptionHandler(AccountExpiredException.class)
    public AjaxResult handleAccountExpiredException(AccountExpiredException e)
    {
        log.error(e.getMessage(), e);
        return AjaxResult.error(e.getMessage());
    }

    @ExceptionHandler(UsernameNotFoundException.class)
    public AjaxResult handleUsernameNotFoundException(UsernameNotFoundException e)
    {
        log.error(e.getMessage(), e);
        return AjaxResult.error(e.getMessage());
    }

/*    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e)
    {
        log.error(e.getMessage(), e);
        return AjaxResult.error(e.getMessage());
    }*/

/*
    */
/**
     * 自定义验证异常
     *//*

    @ExceptionHandler(BindException.class)
    public AjaxResult validatedBindException(BindException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return AjaxResult.error(message);
    }
*/

/*
    */
/**
     * 自定义验证异常
     *//*

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object validExceptionHandler(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return AjaxResult.error(message);
    }
*/

    /**
     * 演示模式异常
     */
    @ExceptionHandler(DemoModeException.class)
    public AjaxResult demoModeException(DemoModeException e)
    {
        return AjaxResult.error("演示模式，不允许操作");
    }
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ConstraintViolationException.class)
    public Object validExceptionHandler(HttpServletResponse response, ConstraintViolationException exs) {
        response.setStatus(200);
        Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
        StringBuffer sb = new StringBuffer();
        for (ConstraintViolation<?> item : violations) {
            String msg = item.getPropertyPath().toString() + ":" + item.getMessage();
            /**打印验证不通过的信息*/
            sb.append(msg);
            log.warn(item.getMessage());
        }
        return new Result<>().fail(425, sb.toString());
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(NumberFormatException.class)
    public Object numberFormatExceptionHandler(HttpServletResponse response, NumberFormatException ex) {
        response.setStatus(200);
        ex.printStackTrace();
        StackTraceElement[] els = ex.getStackTrace();
        String msg = exToMsg(els);
        log.warn(msg + ex.getMessage());
        return new Result<>().fail(426, "参数格式错误");
    }

/*    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ValidException.class)
    public Object validExceptionHandler(ValidException ex) {
        StackTraceElement[] els = ex.getStackTrace();
        String msg = exToMsg(els);
        log.warn(msg + ex.getMessage());
        return new Result<>().fail(440, ex.getMessage());
    }*/

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BindException.class)
    public Object validExceptionHandler(HttpServletResponse response, BindException ex) {
        response.setStatus(200);
        List<ObjectError> errs = ex.getAllErrors();
        String msg = "Bind Exception : ";
        for (ObjectError err : errs) {
            if (err instanceof FieldError) {
                FieldError fieldError = (FieldError) err;
                msg += fieldError.getField() + ":" + err.getDefaultMessage() + " :: ";
            } else {
                msg += err.getDefaultMessage();
            }
        }
        log.warn(msg);
        return new Result<>().fail(450, msg);
    }


    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(IllegalArgumentException.class)
    public Object IllegalArgumentException(IllegalArgumentException ex) {
        ex.printStackTrace();
        StackTraceElement[] els = ex.getStackTrace();
        String msg = exToMsg(els);
        log.error("IllegalArgumentException:{}", msg);
        return new Result<>().fail(427, ex.getMessage());
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(IllegalStateException.class)
    public Object IllegalStateException(IllegalStateException ex) {
        ex.printStackTrace();
        String msg = ex.getMessage();
        StackTraceElement[] els = ex.getStackTrace();
        Arrays.stream(els).forEach(e -> log.error("行号={}:类名={}:方法名={}:文件名={}", e.getLineNumber(), e.getClassName(), e.getMethodName(), e.getFileName()));
        log.error("IllegalStateException:{}", msg);
        return new Result<>().fail(428, ex.getMessage());
    }


    @ResponseStatus(HttpStatus.ACCEPTED)
    @ExceptionHandler(HttpMessageConversionException.class)
    public Object runtimeException(HttpMessageConversionException ex) {
        ex.printStackTrace();
        log.error("HttpMessageConversionException:{}", ex.getRootCause());
        return new Result<>().fail(429, ex.getRootCause().getMessage());
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(RuntimeException.class)
    public Object runtimeException(RuntimeException ex) {
        ex.printStackTrace();
        StackTraceElement[] els = ex.getStackTrace();
        String msg = exToMsg(els);
        msg += ex.getMessage();
        log.error("RuntimeException:{}", ex);
        return new Result<>().fail(505, msg);
    }


    /**
     * 空指针异常
     *
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(NullPointerException.class)
    public Object nullPointException(NullPointerException ex) {
        log.error("NullPointerException:{}", ex);
        return new Result<>().fail(507, ex.getMessage());
    }

/*    *
     * 参数校验异常控制
     *
     * @param exception
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object exceptionHandler(MethodArgumentNotValidException exception) {
//        StringBuilder stringBuilder = new StringBuilder();
        List<ObjectError> errors = exception.getBindingResult().getAllErrors();
        String errorMessages = errors.parallelStream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(","));
//        for (ObjectError error : errors) {
//            stringBuilder.append(",").append(error.getDefaultMessage());
//        }
        exception.printStackTrace();
        log.error("接口数据验证异常{}", errorMessages);
        return new Result<>().fail(304,errorMessages);
    }

/*    *//**
     * Service手动抛出异常控制
     *
     * @param
     * @return
     *//*
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ServiceException.class)
    public Object serviceExceptionHandler(ServiceException serviceException) {
        log.error(Exceptions.getStackTraceAsString(serviceException));
        return new Result<>().fail(serviceException.getErrorCode(), serviceException.getErrorMsg());
    }*/

/*    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(SqlRuntimeException.class)
    public Object sqlRuntimeExceptionHandler(SqlRuntimeException ex) {
        StackTraceElement[] els = ex.getStackTrace();
        String msg = exToMsg(els);
        log.warn("{} --- {}", msg, ex.getMessage());
        return new Result<>().fail(510, ex.getMessage());
    }*/

    @ResponseStatus(HttpStatus.CREATED)
    @ExceptionHandler(InvocationTargetException.class)
    public Object invocationTargetException(InvocationTargetException ex) {
        log.error(ex.getMessage() + ":{}", ex);
        return new Result<>().fail(600, ex.getMessage());
    }

/*    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BaseException.class)
    public Object baseExceptionHandler(BaseException ex) {
        log.error("{}:{}", ex.getMessage(), ex);
        return new Result<>().fail(650, ex.getMessage());
    }*/

    @ResponseStatus(HttpStatus.ACCEPTED)
    @ExceptionHandler(Exception.class)
    public Object otherExceptionHandler(Exception ex) {
        ex.printStackTrace();
        log.error("异常{}", ex.getMessage());
        return new Result<>().fail(660, ex.getMessage());
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(AccessDeniedException.class)
    public Object accessDeniedExceptionHandler(AccessDeniedException ex) {
        ex.printStackTrace();
        log.error("异常{}", ex.getMessage());
        return new Result<>(REALNAME_INCORRECT);
    }
}
